package com.wq.spi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: wangqiang20995
 * @Date:2019/5/16
 * @Description:
 * @Resource:
 */
public class SpiExtensionLoader<T> {

    private Class<T> interfaceType;

    //存放spi修饰的接口类与其默认别名的映射
    private static final Map<Class<?>, String> spiDefaultNameCache = new ConcurrentHashMap<>();

    //存放spi修饰的接口类和这个loader的映射关系
    private static final Map<Class<?>, SpiExtensionLoader<?>> extensionLoaderCache = new ConcurrentHashMap<>();

    //spi的接口和其实现的映射
    private static final Map<Class<?>, Holder<Map<String, Class<?>>>> spiImplementations = new ConcurrentHashMap<>();

    //key=spi接口实现的name，value为spi接口实现的实例
    private static final Map<String,Object> spiInstance = new ConcurrentHashMap<>();


    private static final String META_SPI_PATH = "META-INF/spi/";


    /**
     * @param clazz:被spi修饰的接口
     */
    private SpiExtensionLoader(Class<T> clazz) {
        this.interfaceType = clazz;
    }


    public static <P> SpiExtensionLoader<P> spiExtensionLoader(Class<P> clazz){
        SpiExtensionLoader<P> loader = (SpiExtensionLoader<P>) extensionLoaderCache.get(clazz);
        if( loader == null){
            loader = new SpiExtensionLoader<>(clazz);
            extensionLoaderCache.putIfAbsent(clazz,loader);
            return loader;
        }

        return loader;
    }

    private <T> T instance(Class<T> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<T> constructor = clazz.getDeclaredConstructor(new Class[]{});
        if(constructor.getModifiers() == Modifier.PUBLIC){
            T t = constructor.newInstance(new Object[]{});
            return t;
        }
        return null;
    }
    /**
     * @param <T>   被SPI修饰的接口
     * @return 被SPI修饰的接口的实现类
     */
    public <T> T getSpiImplementation() {
        Class<T> clazz = (Class<T>) this.interfaceType;

        if (!clazz.isInterface()) {
            throw new IllegalArgumentException("class[" + clazz.getName() + "] must be an interface");
        }

        SPI spi = clazz.getDeclaredAnnotation(SPI.class);
        if (spi == null) {
            throw new IllegalStateException("interface[" + clazz.getName() + "] must be decorated with Annotation[" + SPI.class.getName() + "]");
        }

        //设置spi接口的默认实现
        String defaultValue = spi.defaultValue();
        spiDefaultNameCache.putIfAbsent(clazz, defaultValue);

        //查找spi接口的配置定义文件
        String interfaceName = clazz.getName();
        String spiFile = META_SPI_PATH + interfaceName;

        try{
            loadSpiClasses(spiFile);

            Holder<Map<String,Class<?>>> holder = spiImplementations.get(clazz);
            if(holder == null){
                return null;
            }

            Class<T> imple = (Class<T>) holder.get().get(defaultValue);

            T instance = instance(imple);

            spiInstance.putIfAbsent(defaultValue,instance);
            return instance;
        }catch (Exception e){
            System.err.println("load spi class failed");
        }

        return null;
    }

    private void loadSpiClasses(String spiFile) throws ClassNotFoundException {
        if (spiFile == null || spiFile.trim().length() == 0) {
            return;
        }

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        try {
            Enumeration<URL> urls = classLoader.getResources(spiFile);

            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                loadSpiFile(url,classLoader);
            }
        } catch (IOException e) {
            System.err.println("load spi file[" + spiFile + "] failed");
        }
    }

    private void loadSpiFile(URL url,ClassLoader classLoader) throws ClassNotFoundException {

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("#") || line.indexOf("=") < 0) {
                    continue;
                }

                String[] config = line.split("=");
                loadConfigClass(config[0],config[1],classLoader);
            }

        } catch (IOException e) {
            System.err.println("open file[" + url + "] failed");
        }
    }

    private void loadConfigClass(String name, String imple, ClassLoader classLoader) throws ClassNotFoundException {
        Class<?> clazz = Class.forName(imple, false, classLoader);
        if (!this.interfaceType.isAssignableFrom(clazz)) {
            throw new IllegalArgumentException("class[" + imple + "] is not an implementation of interface[" + interfaceType.getName() + "]");
        }

        Holder<Map<String,Class<?>>> holder = spiImplementations.get(interfaceType);
        if(holder == null){
            holder = new Holder<>();
            spiImplementations.put(interfaceType,holder);
        }

        if(holder.get() == null){
            holder.set(new ConcurrentHashMap<>());
        }

        Map<String,Class<?>> map = holder.get();
        map.putIfAbsent(name,clazz);

    }

    public static void main(String[] args){
        SpiExtensionLoader<Service> loader = SpiExtensionLoader.spiExtensionLoader(Service.class);
        Service service = loader.getSpiImplementation();
        service.show();
    }
}
